package com.yx.elastic.elasticcm.base.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.yx.elastic.elasticcm.util.StringUtils;
import com.yx.elastic.elasticcm.base.ISearch;
import com.yx.elastic.elasticcm.base.ISearchException;
import com.yx.elastic.elasticcm.client.MClientManager;
import com.yx.elastic.elasticcm.orm.ElasORM;
import com.yx.elastic.elasticcm.orm.ElasORMClass;
import com.yx.elastic.elasticcm.orm.ElasObject;
import org.apache.http.util.EntityUtils;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.admin.indices.get.GetIndexRequest;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.IndicesClient;
import org.elasticsearch.client.Response;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.List;

import static com.yx.elastic.elasticcm.base.ISearchExceptionCode.CONNECT_ERROR;
import static com.yx.elastic.elasticcm.base.ISearchExceptionCode.NOT_INDEX;
import static com.yx.elastic.elasticcm.base.ISearchExceptionCode.OTHER_ERROR;

/**
 * Created by chenzp on 18-8-1.
 */
@Component
public class ISearchImpl<T> implements ISearch<T> {

    private static Logger logger = LoggerFactory.getLogger(MClientManager.class);

    private String HEAD = "HEAD";
    private String GET = "GET";
    private String DELETE = "DELETE";
    private String POST = "POST";
    private String PUT = "PUT";

    @Autowired
    private MClientManager mClientManager;


    @Override
    public boolean createIndex(JSONObject settings, JSONObject mappings) throws ISearchException {
        try {

            IndicesClient indices = mClientManager.getHighLevelClient().indices();
            ElasORMClass elasORMClass = ElasORM.typeToOrmClass(this.getClass().getGenericSuperclass());
            boolean exists = indices.exists(new GetIndexRequest().indices(elasORMClass.getIndex()));
            if (exists) {
                indices.delete(new DeleteIndexRequest().indices(elasORMClass.getIndex()));
            }
            indices.create(new CreateIndexRequest().index(elasORMClass.getIndex()).mapping(elasORMClass.getType(),mappings).settings(settings));

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            throw new ISearchException(OTHER_ERROR);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            throw new ISearchException(OTHER_ERROR);
        } catch (InstantiationException e) {
            e.printStackTrace();
            throw new ISearchException(OTHER_ERROR);
        } catch (IOException e) {
            e.printStackTrace();
            throw new ISearchException(CONNECT_ERROR);
        }


        return false;
    }

    @Override
    public boolean hasIndex(String index) throws ISearchException {
        try {
            boolean exists = mClientManager.getHighLevelClient().indices().exists(new GetIndexRequest().indices(index));
            return exists;
        } catch (IOException e) {
            e.printStackTrace();
            throw new ISearchException(CONNECT_ERROR);
        }

    }

    @Override
    public boolean hasIndex() throws ISearchException {
        try {
            ElasORMClass elasORMClass = ElasORM.typeToOrmClass(this.getClass().getGenericSuperclass());
            boolean exists = mClientManager.getHighLevelClient().indices().exists(new GetIndexRequest().indices(elasORMClass.getIndex()));
            return exists;
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            throw new ISearchException(OTHER_ERROR);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            throw new ISearchException(OTHER_ERROR);
        } catch (InstantiationException e) {
            e.printStackTrace();
            throw new ISearchException(OTHER_ERROR);
        } catch (IOException e) {
            e.printStackTrace();
            throw new ISearchException(CONNECT_ERROR);
        }
    }


    @Override
    public boolean deleteIndex() throws ISearchException {
        try {
            ElasORMClass elasORMClass = ElasORM.typeToOrmClass(this.getClass().getGenericSuperclass());
            IndicesClient indices = mClientManager.getHighLevelClient().indices();

            boolean exists = indices.exists(new GetIndexRequest().indices(elasORMClass.getIndex()));

            if (exists) {
                indices.delete(new DeleteIndexRequest().indices(elasORMClass.getIndex()));
            }
            return true;
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            throw new ISearchException(OTHER_ERROR);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            throw new ISearchException(OTHER_ERROR);
        } catch (InstantiationException e) {
            e.printStackTrace();
            throw new ISearchException(OTHER_ERROR);
        } catch (IOException e) {
            e.printStackTrace();
            throw new ISearchException(CONNECT_ERROR);
        }
    }


    @Override
    public boolean deleteIndex(String index) throws ISearchException {
        try {
            IndicesClient indices = mClientManager.getHighLevelClient().indices();

            boolean exists = indices.exists(new GetIndexRequest().indices(index));

            if (exists) {
                indices.delete(new DeleteIndexRequest().indices(index));
            }
            return true;
        } catch (IOException e) {
            e.printStackTrace();
            throw new ISearchException(CONNECT_ERROR);
        }
    }

    @Override
    public JSONObject getApi() throws ISearchException {
        ElasORMClass elasORMClass = null;
        try {
            elasORMClass = ElasORM.typeToOrmClass(this.getClass().getGenericSuperclass());
            Response response = mClientManager.getClient().performRequest(GET, elasORMClass.getIndex());
            String s = EntityUtils.toString(response.getEntity());
            JSONObject jsonObject = JSON.parseObject(s);
            return jsonObject.getJSONObject(elasORMClass.getIndex());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            throw new ISearchException(OTHER_ERROR);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            throw new ISearchException(OTHER_ERROR);
        } catch (InstantiationException e) {
            e.printStackTrace();
            throw new ISearchException(OTHER_ERROR);
        } catch (IOException e) {
            e.printStackTrace();
            throw new ISearchException(CONNECT_ERROR);
        }
    }

    @Override
    public JSONObject getApi(String index) throws ISearchException {

        if (StringUtils.isNullAndEmpty(index)) {
            throw new ISearchException(NOT_INDEX);
        }
        Response response = null;
        try {
            response = mClientManager.getClient().performRequest(GET, index);
            String s = EntityUtils.toString(response.getEntity());


            JSONObject jsonObject = JSON.parseObject(s);
            return jsonObject.getJSONObject(index);
        } catch (IOException e) {
            e.printStackTrace();
            throw new ISearchException(CONNECT_ERROR);
        }

    }


    @Override
    public IndexResponse index(T object) throws ISearchException {
        try {
            ElasObject elasObject = ElasORM.conversion(object);


            IndexRequest indexRequest = new IndexRequest(elasObject.getIndex(), elasObject.getType(), elasObject.getId())
                    .source(elasObject.getJson().toJSONString(), XContentType.JSON);
            return mClientManager.getHighLevelClient().index(indexRequest);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
            throw new ISearchException(OTHER_ERROR);
        } catch (InvocationTargetException e) {
            e.printStackTrace();
            throw new ISearchException(OTHER_ERROR);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            throw new ISearchException(OTHER_ERROR);
        } catch (IOException e) {
            e.printStackTrace();
            throw new ISearchException(CONNECT_ERROR);
        }
    }


    @Override
    public BulkResponse indexBatch(List<T> list) throws ISearchException {
        BulkRequest bulkRequest = new BulkRequest();

        try {
            for (T t : list) {
                ElasObject elasObject = ElasORM.conversion(t);
                IndexRequest indexRequest = new IndexRequest(elasObject.getIndex(), elasObject.getType(), elasObject.getId());
                indexRequest.source(elasObject.getJson().toJSONString(), XContentType.JSON);
                bulkRequest.add(indexRequest);
            }
            return mClientManager.getHighLevelClient().bulk(bulkRequest);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
            throw new ISearchException(OTHER_ERROR);
        } catch (InvocationTargetException e) {
            e.printStackTrace();
            throw new ISearchException(OTHER_ERROR);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            throw new ISearchException(OTHER_ERROR);
        } catch (IOException e) {
            e.printStackTrace();
            throw new ISearchException(CONNECT_ERROR);
        }
    }

    @Override
    public UpdateResponse update(T object) throws ISearchException {
        try {
            ElasObject elasObject = ElasORM.conversion(object);
            UpdateRequest updateRequest = new UpdateRequest(elasObject.getIndex(), elasObject.getType(), elasObject.getId())
                    .doc(elasObject.getJson().toJSONString(), XContentType.JSON);
            return mClientManager.getHighLevelClient().update(updateRequest);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
            throw new ISearchException(OTHER_ERROR);
        } catch (InvocationTargetException e) {
            e.printStackTrace();
            throw new ISearchException(OTHER_ERROR);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            throw new ISearchException(OTHER_ERROR);
        } catch (IOException e) {
            e.printStackTrace();
            throw new ISearchException(OTHER_ERROR);
        }
    }


    @Override
    public BulkResponse updateBatch(List<T> list) throws ISearchException {

        if (list == null || list.size() == 0) {
            return null;
        }

        BulkRequest bulkRequest = new BulkRequest();
        try {
            for (T t : list) {

                ElasObject elasObject = ElasORM.conversion(t);
                UpdateRequest updateRequest = new UpdateRequest(elasObject.getIndex(), elasObject.getType(), elasObject.getId());
                updateRequest.doc(elasObject.getJson().toJSONString(), XContentType.JSON);
                bulkRequest.add(updateRequest);
            }

            BulkResponse bulk = mClientManager.getHighLevelClient().bulk(bulkRequest);
            return bulk;

        } catch (NoSuchMethodException e) {
            e.printStackTrace();
            throw new ISearchException(OTHER_ERROR);
        } catch (InvocationTargetException e) {
            e.printStackTrace();
            throw new ISearchException(OTHER_ERROR);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            throw new ISearchException(OTHER_ERROR);
        } catch (IOException e) {
            e.printStackTrace();
            throw new ISearchException(CONNECT_ERROR);
        }
    }

    @Override
    public T get(T object) throws ISearchException {
        try {
            ElasObject elasObject = ElasORM.conversion(object);
            GetRequest getRequest = new GetRequest(elasObject.getIndex(), elasObject.getType(), elasObject.getId());
            GetResponse documentFields = mClientManager.getHighLevelClient().get(getRequest);
            return (T) ElasORM.jsonToOrmObject(documentFields, object);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
            throw new ISearchException(OTHER_ERROR);
        } catch (InvocationTargetException e) {
            e.printStackTrace();
            throw new ISearchException(OTHER_ERROR);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            throw new ISearchException(OTHER_ERROR);
        } catch (IOException e) {
            e.printStackTrace();
            throw new ISearchException(CONNECT_ERROR);
        }
    }

    @Override
    public T getById(Object id) throws ISearchException {
        try {
            Object o = ElasORM.typeToObj(this.getClass().getGenericSuperclass());
            ElasORMClass elasORMClass = ElasORM.typeToOrmClass(this.getClass().getGenericSuperclass());
            GetRequest getRequest = new GetRequest(elasORMClass.getIndex(), elasORMClass.getType(), id.toString());
            GetResponse documentFields = mClientManager.getHighLevelClient().get(getRequest);
            return (T) ElasORM.jsonToOrmObject(documentFields, o);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            throw new ISearchException(OTHER_ERROR);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            throw new ISearchException(OTHER_ERROR);
        } catch (InstantiationException e) {
            e.printStackTrace();
            throw new ISearchException(OTHER_ERROR);
        } catch (IOException e) {
            e.printStackTrace();
            throw new ISearchException(CONNECT_ERROR);
        }
    }

    @Override
    public DeleteResponse delete(T object) throws ISearchException {
        try {
            ElasObject elasObject = ElasORM.conversion(object);
            DeleteRequest deleteRequest = new DeleteRequest(elasObject.getIndex(), elasObject.getType(), elasObject.getId());
            DeleteResponse delete = mClientManager.getHighLevelClient().delete(deleteRequest);
            return delete;
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
            throw new ISearchException(OTHER_ERROR);
        } catch (InvocationTargetException e) {
            e.printStackTrace();
            throw new ISearchException(OTHER_ERROR);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            throw new ISearchException(OTHER_ERROR);
        } catch (IOException e) {
            e.printStackTrace();
            throw new ISearchException(CONNECT_ERROR);
        }

    }


    @Override
    public DeleteResponse delete(String index, String type, String id) throws ISearchException {
        DeleteRequest deleteRequest = new DeleteRequest(index, type, id);

        try {
            return mClientManager.getHighLevelClient().delete(deleteRequest);
        } catch (IOException e) {
            e.printStackTrace();
            throw new ISearchException(CONNECT_ERROR);
        }
    }


    @Override
    public DeleteResponse deleteById(Object id) throws ISearchException {

        try {
            ElasORMClass elasORMClass = ElasORM.typeToOrmClass(this.getClass().getGenericSuperclass());
            DeleteRequest deleteRequest = new DeleteRequest(elasORMClass.getIndex(), elasORMClass.getType(), id.toString());
            return mClientManager.getHighLevelClient().delete(deleteRequest);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            throw new ISearchException(OTHER_ERROR);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            throw new ISearchException(OTHER_ERROR);
        } catch (InstantiationException e) {
            e.printStackTrace();
            throw new ISearchException(OTHER_ERROR);
        } catch (IOException e) {
            e.printStackTrace();
            throw new ISearchException(CONNECT_ERROR);
        }
    }

    @Override
    public BulkResponse deleteBatch(List<T> list) throws ISearchException {

        try {
            BulkRequest bulkRequest = new BulkRequest();
            for (T t : list) {
                ElasObject elasObject = ElasORM.conversion(t);
                bulkRequest.add(new DeleteRequest(elasObject.getIndex(), elasObject.getType(), elasObject.getId()));
            }

            BulkResponse bulk = mClientManager.getHighLevelClient().bulk(bulkRequest);

            return bulk;

        } catch (NoSuchMethodException e) {
            e.printStackTrace();
            throw new ISearchException(OTHER_ERROR);
        } catch (InvocationTargetException e) {
            e.printStackTrace();
            throw new ISearchException(OTHER_ERROR);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            throw new ISearchException(OTHER_ERROR);
        } catch (IOException e) {
            e.printStackTrace();
            throw new ISearchException(CONNECT_ERROR);
        }
    }


    @Override
    public BulkResponse deleteBatchById(String index, String type, List<Object> ids) throws ISearchException {
        BulkRequest bulkRequest = new BulkRequest();
        for (Object id : ids) {
            bulkRequest.add(new DeleteRequest(index, type, id.toString()));
        }
        try {
            BulkResponse bulk = mClientManager.getHighLevelClient().bulk(bulkRequest);
            return bulk;
        } catch (IOException e) {
            e.printStackTrace();
            throw new ISearchException(CONNECT_ERROR);
        }
    }


    @Override
    public BulkResponse deleteBatchById(List<Object> ids) throws ISearchException {

        if (ids == null || ids.size() == 0) {
            return null;
        }

        try {
            ElasORMClass elasORMClass = ElasORM.typeToOrmClass(this.getClass().getGenericSuperclass());
            BulkRequest bulkRequest = new BulkRequest();
            for (Object id : ids) {
                bulkRequest.add(new DeleteRequest(elasORMClass.getIndex(), elasORMClass.getType(), id.toString()));
            }
            BulkResponse bulk = mClientManager.getHighLevelClient().bulk(bulkRequest);
            return bulk;
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            throw new ISearchException(OTHER_ERROR);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            throw new ISearchException(OTHER_ERROR);
        } catch (InstantiationException e) {
            e.printStackTrace();
            throw new ISearchException(OTHER_ERROR);
        } catch (IOException e) {
            e.printStackTrace();
            throw new ISearchException(CONNECT_ERROR);
        }
    }


    @Override
    public SearchResponse query(BoolQueryBuilder boolQueryBuilder) throws ISearchException {
        try {
            ElasORMClass elasORMClass = ElasORM.typeToOrmClass(this.getClass().getGenericSuperclass());
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder().query(boolQueryBuilder);
            SearchRequest searchRequest = new SearchRequest(elasORMClass.getIndex()).types(elasORMClass.getType()).source(searchSourceBuilder);
            SearchResponse searchResponse = mClientManager.getHighLevelClient().search(searchRequest);
            return searchResponse;
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            throw new ISearchException(OTHER_ERROR);
        } catch (InstantiationException e) {
            e.printStackTrace();
            throw new ISearchException(OTHER_ERROR);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            throw new ISearchException(OTHER_ERROR);
        } catch (IOException e) {
            e.printStackTrace();
            throw new ISearchException(CONNECT_ERROR);
        }
    }


    @Override
    public SearchResponse query(SearchSourceBuilder searchSourceBuilder) throws ISearchException {
        try {
            ElasORMClass elasORMClass = ElasORM.typeToOrmClass(this.getClass().getGenericSuperclass());
            SearchRequest searchRequest = new SearchRequest(elasORMClass.getIndex()).types(elasORMClass.getType()).source(searchSourceBuilder);
           return mClientManager.getHighLevelClient().search(searchRequest);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            throw new ISearchException(OTHER_ERROR);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            throw new ISearchException(OTHER_ERROR);
        } catch (InstantiationException e) {
            e.printStackTrace();
            throw new ISearchException(OTHER_ERROR);
        } catch (IOException e) {
            e.printStackTrace();
            throw new ISearchException(CONNECT_ERROR);
        }
    }


    @Override
    public SearchResponse query(SearchRequest searchRequest) throws ISearchException {
        try {
            ElasORMClass elasORMClass = ElasORM.typeToOrmClass(this.getClass().getGenericSuperclass());
           return mClientManager.getHighLevelClient().search(searchRequest);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            throw new ISearchException(OTHER_ERROR);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            throw new ISearchException(OTHER_ERROR);
        } catch (InstantiationException e) {
            e.printStackTrace();
            throw new ISearchException(OTHER_ERROR);
        } catch (IOException e) {
            e.printStackTrace();
            throw new ISearchException(CONNECT_ERROR);
        }
    }
}
